<?php
// +----------------------------------------------------------------------
// | RXThinkCMF框架 [ RXThinkCMF ]
// +----------------------------------------------------------------------
// | 版权所有 2017~2020 南京RXThinkCMF研发中心
// +----------------------------------------------------------------------
// | 官方网站: http://www.rxthink.cn
// +----------------------------------------------------------------------
// | Author: 牧羊人 <1175401194@qq.com>
// +----------------------------------------------------------------------

namespace app\api\controller;

use app\api\logic\LogicalProcess;
use app\api\model\Member;
use app\BaseController;
use think\cache\driver\Redis;
use think\exception\HttpResponseException;
use think\facade\Cache;
use think\model\Collection;
use think\Request;
use think\Response;
use think\response\Json;

/**
 * 后台-控制器
 * @author 牧羊人
 * @since 2020/11/14
 * Class Backend
 * @package app\admin\controller
 */
class Api extends BaseController
{
    //方法名称
    protected $action;
    // 用户ID
    protected $userId;
    //权限
    protected $auth = [];
    protected $responseType = 'json';
    //配置
    protected $config = [];

    /**
     * 初始化
     * @author 牧羊人
     * @since 2020/11/14
     */
    protected function initialize()
    {
        parent::initialize(); // TODO: Change the autogenerated stub
        //设定header
        header('Access-Control-Allow-Origin: *');
        header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
        header('Access-Control-Allow-Methods: GET, POST, PUT,DELETE');
        $this->action = $this->request->action(); //获取方法名
        $this->config = config('configure');;
        // 获取Token
        $token = request()->header("token");

        //获取请求时间戳
        $timestamp = request()->header("timestamp");
        if ($token) {
            // JWT解密token
            $jwt = new \Jwt();
            $this->userId = $jwt->verifyToken($token);

            if( Cache::store("redis")->get('member_token_'.$this->userId) != $token){
                echo json_encode(message('请先登录',false,[],401),JSON_UNESCAPED_UNICODE);die;
            }
        }

        //获取当前的规则
        foreach ($this->auth as $key => $value) {
            if (in_array('*', $value['actions'], true) || in_array($this->action, $value['actions'], true)) {
                $roles = $this->auth[$key]['roles'];
                $allow = $this->auth[$key]['allow'];
                break;
            }
        }
        if (empty($roles) || !in_array($roles,['all','no_all','login'],true)) $roles = 'login';
        if (empty($allow)) $allow = [];


        //验证接口请求时间
        $requestTime = !empty($this->config['timestamp']) ? $this->config['timestamp'] : 0;
        if(!in_array('no_checkTime', $allow, true) && $requestTime > 0){
            $checkRequestResult = check_time($timestamp,$requestTime);
            if(!$checkRequestResult['code']){
                $this->error($checkRequestResult['msg'],[],403);
                echo json_encode(message($checkRequestResult['msg'],false,[],403),JSON_UNESCAPED_UNICODE);die;
            }
        }

        //验证签名
        $is_checksign = $this->config['is_checksign'];
        $params = $this->request->param();

        $signature_key = $this->config['signature_key'];
        if(!in_array('no_checkSign',$allow,true) && $is_checksign == 'on'){
            $checkSign = check_sign($params,$signature_key);
            if(!$checkSign['code']){
                echo json_encode(message($checkSign['msg'],false,[],405),JSON_UNESCAPED_UNICODE);die;
            }
        }


        if($roles == 'no_all'){
            $this->error('禁止访问',[],400);
            echo json_encode(message('禁止访问',false,[],400),JSON_UNESCAPED_UNICODE);die;
        }elseif($roles == 'login'){
            if(!$this->userId){
                echo json_encode(message('请先登录',false,[],401),JSON_UNESCAPED_UNICODE);die;
            }
        }
        // 初始化配置
        $this->initConfig();
    }

    /**
     * 初始化配置
     * @author 牧羊人
     * @since 2020/11/14
     */
    public function initConfig()
    {
        // 定义是否GET请求
        defined('IS_GET') or define('IS_GET', $this->request->isGet());

        // 定义是否POST请求
        defined('IS_POST') or define('IS_POST', $this->request->isPost());

        // 定义是否AJAX请求
        defined('IS_AJAX') or define('IS_AJAX', $this->request->isAjax());

        // 定义是否PAJAX请求
        defined('IS_PJAX') or define('IS_PJAX', $this->request->isPjax());

        // 定义是否PUT请求
        defined('IS_PUT') or define('IS_PUT', $this->request->isPut());

        // 定义是否DELETE请求
        defined('IS_DELETE') or define('IS_DELETE', $this->request->isDelete());

        // 定义是否HEAD请求
        defined('IS_HEAD') or define('IS_HEAD', $this->request->isHead());

        // 定义是否PATCH请求
        defined('IS_PATCH') or define('IS_PATCH', $this->request->isPatch());

        // 定义是否为手机访问
        defined('IS_MOBILE') or define('IS_MOBILE', $this->request->isMobile());

        // 定义是否为cli
        defined('IS_CLI') or define('IS_CLI', $this->request->isCli());

        // 定义是否为cgi
        defined('IS_CGI') or define('IS_CGI', $this->request->isCgi());

        // 图片域名
        defined('IMG_URL') or define('IMG_URL', env('DOMAIN.IMG_URL'));

        // 分页参数
        defined('PAGE') or define('PAGE', getter(request()->param(), "page", 1));
        defined('PERPAGE') or define('PERPAGE', getter(request()->param(), "limit", 10));
    }

    /**
     * 获取全部参数
     * @return array|mixed
     */
    public function param(){
        $param =  $this->request->param();
        if($this->config['is_interface'] == 'on'){
            return $this->decrypt($param);
        }
        return $param;
    }


    /**
     * 操作成功返回的数据
     * @param string $msg    提示信息
     * @param mixed  $data   要返回的数据
     * @param array|string $encryption 要加密的字段
     * @param int    $code   错误码，默认为1
     * @param string $type   输出类型
     * @param array  $header 发送的 Header 信息
     */
    protected function success($msg = '', $data = null, $encryption=[], $code = 1, $type = null, array $header = [])
    {
        if(!empty($encryption) && $this->config['is_interface'] == 'on'){
            $data = $this->encryption($encryption,$data);
        }
        $this->result($msg, $data, $code, $type, $header);
    }


    /**
     * 操作失败返回的数据
     * @param string $msg    提示信息
     * @param mixed  $data   要返回的数据
     * @param int    $code   错误码，默认为0
     * @param string $type   输出类型
     * @param array  $header 发送的 Header 信息
     */
    protected function error($msg = '', $data = null, $code = 0, $type = null, array $header = [])
    {
        $this->result($msg, $data, $code, $type, $header);
    }


    /**
     * 返回封装后的 API 数据到客户端
     * @access protected
     * @param mixed  $msg    提示信息
     * @param mixed  $data   要返回的数据
     * @param int    $code   错误码，默认为0
     * @param string $type   输出类型，支持json/xml/jsonp
     * @param array  $header 发送的 Header 信息
     * @return void
     * @throws HttpResponseException
     */
    protected function result($msg, $data = null, $code = 0, $type = null, array $header = [])
    {
        $result = [
            'code' => $code,
            'msg'  => $msg,
            'data' => $data,
        ];
        // 如果未设置类型则自动判断
        $type = $type ? $type : ($this->request->param(config('var_jsonp_handler')) ? 'jsonp' : $this->responseType);

        if (isset($header['statuscode'])) {
            $code = $header['statuscode'];
            unset($header['statuscode']);
        } else {
            //未设置状态码,根据code值判断
            $code = $code >= 1000 || $code < 200 ? 200 : $code;
        }
        $response = Response::create($result, $type, $code)->header($header);
        throw new HttpResponseException($response);
    }


    /**
     * 加密方法
     * @param $column 要加密的字段
     * @param $data 数据
     */
    public function encryption($column,$data){
        $key = $this->config['interface_key'];
        if(is_array($column)){
            foreach($data as $k=>$v){
                if(is_array($v)){
                    $data[$k] = $this->encryption($column,$v);
                }else{
                    foreach($column as $k1=>$v1){
                        if($k == $v1){
                            $data[$k] = $this->lock($v,$key);
                        }
                    }
                }
            }
        }else{
            foreach($data as $k=>$v){
                if(is_array($v)){
                    $data[$k] = $this->encryption($column,$v);
                }else{
                    if($k === $column){
                        $data[$k] = $this->lock($v,$key);
                    }
                }
            }
        }
        return $data;
    }


    /**
     * 解密方法
     * @param $data 数据
     */
    public function decrypt($data){
        $key = $this->config['interface_key'];
        foreach($data as $k=>$v){
            if(is_array($v)){
                $data[$k] = $this->decrypt($v);
            }else{
                if(strpos($v,'****') !== false){
                    $str = str_replace('****','',$v);
                    $data[$k] = $this->unlock($str,$key);
                }
            }
        }
        return $data;
    }


    /**
     * 加密算法
     */
    public function lock($txt,$key=null){
        $txt = $txt.$key;
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=+";
        $nh = rand(0,64);
        $ch = $chars[$nh];
        $mdKey = md5($key.$ch);
        $mdKey = substr($mdKey,$nh%8, $nh%8+7);
        $txt = base64_encode($txt);
        $tmp = '';
        $i=0;$j=0;$k = 0;
        for ($i=0; $i<strlen($txt); $i++) {
            $k = $k == strlen($mdKey) ? 0 : $k;
            $j = ($nh+strpos($chars,$txt[$i])+ord($mdKey[$k++]))%64;
            $tmp .= $chars[$j];
        }
        return '****'.urlencode(base64_encode($ch.$tmp));
    }


    /**
     * 解密算法
     */

    public function unlock($txt,$key=null){
        $txt = base64_decode(urldecode($txt));
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=+";
        $ch = $txt[0];
        $nh = strpos($chars,$ch);
        $mdKey = md5($key.$ch);
        $mdKey = substr($mdKey,$nh%8, $nh%8+7);
        $txt = substr($txt,1);
        $tmp = '';
        $i=0;$j=0; $k = 0;
        for ($i=0; $i<strlen($txt); $i++) {
            $k = $k == strlen($mdKey) ? 0 : $k;
            $j = strpos($chars,$txt[$i])-$nh - ord($mdKey[$k++]);
            while ($j<0) $j+=64;
            $tmp .= $chars[$j];
        }
        $str = base64_decode($tmp);
        return substr($str,0,strlen($str)-strlen($key));
    }

}